#include "rdpsession.h"
#include <QDebug>
static BOOL peer_context_new(freerdp_peer* client, rdpContext* ctx)
{
	testPeerContext* context = (testPeerContext*)ctx;
	if (!(context->rfx_context = rfx_context_new(TRUE)))
		goto fail_rfx_context;

	if (!rfx_context_reset(context->rfx_context, SAMPLE_SERVER_DEFAULT_WIDTH,
	                       SAMPLE_SERVER_DEFAULT_HEIGHT))
		goto fail_rfx_context;

	context->rfx_context->mode = RLGR3;
	rfx_context_set_pixel_format(context->rfx_context, PIXEL_FORMAT_RGB24);

	if (!(context->nsc_context = nsc_context_new()))
		goto fail_nsc_context;

	if (!nsc_context_set_parameters(context->nsc_context, NSC_COLOR_FORMAT, PIXEL_FORMAT_RGB24))
		goto fail_stream_new;

	if (!(context->s = Stream_New(NULL, 65536)))
		goto fail_stream_new;


	context->vcm = WTSOpenServerA((LPSTR)client->context);

	if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
		goto fail_open_server;

	return TRUE;
fail_open_server:
	context->vcm = NULL;
	Stream_Free(context->s, TRUE);
	context->s = NULL;
fail_stream_new:
	nsc_context_free(context->nsc_context);
	context->nsc_context = NULL;
fail_nsc_context:
	rfx_context_free(context->rfx_context);
	context->rfx_context = NULL;
fail_rfx_context:
	return FALSE;
}

static void peer_context_free(freerdp_peer* client, rdpContext* ctx)
{
	testPeerContext* context = (testPeerContext*)ctx;
	WINPR_UNUSED(client);

	if (context)
	{
		if (context->debug_channel_thread)
		{
			SetEvent(context->stopEvent);
			WaitForSingleObject(context->debug_channel_thread, INFINITE);
			CloseHandle(context->debug_channel_thread);
		}

		Stream_Free(context->s, TRUE);
		free(context->bg_data);
		rfx_context_free(context->rfx_context);
		nsc_context_free(context->nsc_context);

		if (context->debug_channel)
			WTSVirtualChannelClose(context->debug_channel);


		if (context->encomsp)
			encomsp_server_context_free(context->encomsp);

		WTSCloseServer((HANDLE)context->vcm);
	}
}

static BOOL peer_init(freerdp_peer* client)
{
	client->ContextSize = sizeof(testPeerContext);
	client->ContextNew = peer_context_new;
	client->ContextFree = peer_context_free;
	return freerdp_peer_context_new(client);
}

rdpsession::rdpsession(QObject *parent ): QObject(parent)
{
	
}
rdpsession::~rdpsession()
{
    clientThread.join();
}

int rdpsession::initSettings()
{
	
	testPeerContext* context;
	freerdp_peer* client = (freerdp_peer*)clientPtr;

	if (!peer_init(client))
	{
		freerdp_peer_free(client);
		return -1;
	}

	/* Initialize the real server settings here */
	client->settings->CertificateFile = _strdup(m_tlsCertificate.string().data());
	client->settings->PrivateKeyFile = _strdup(m_tlsCertificateKey.string().data());
	client->settings->RdpKeyFile = _strdup(m_tlsCertificateKey.string().data());

	if (!client->settings->CertificateFile || !client->settings->PrivateKeyFile ||
	    !client->settings->RdpKeyFile)
	{
		WLog_ERR(TAG, "Memory allocation failed (strdup)");
		freerdp_peer_free(client);
		return -1;
	}

	client->settings->RdpSecurity = TRUE;
	client->settings->TlsSecurity = TRUE;
	client->settings->NlaSecurity = FALSE;
	client->settings->EncryptionLevel = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE;
	/* client->settings->EncryptionLevel = ENCRYPTION_LEVEL_HIGH; */
	/* client->settings->EncryptionLevel = ENCRYPTION_LEVEL_LOW; */
	/* client->settings->EncryptionLevel = ENCRYPTION_LEVEL_FIPS; */
	client->settings->RemoteFxCodec = TRUE;
	client->settings->NSCodec = TRUE;
	client->settings->ColorDepth = 32;
	client->settings->SuppressOutput = TRUE;
	client->settings->RefreshRect = TRUE;
	client->PostConnect = tf_peer_post_connect;
	client->Activate = tf_peer_activate;
	client->input->SynchronizeEvent = tf_peer_synchronize_event;
	client->input->KeyboardEvent = tf_peer_keyboard_event;
	client->input->UnicodeKeyboardEvent = tf_peer_unicode_keyboard_event;
	client->input->MouseEvent = tf_peer_mouse_event;
	client->input->ExtendedMouseEvent = tf_peer_extended_mouse_event;
	client->update->RefreshRect = tf_peer_refresh_rect;
	client->update->SuppressOutput = tf_peer_suppress_output;
	client->settings->MultifragMaxRequestSize = 0xFFFFFF; /* FIXME */
	client->Initialize(client);
	context = (testPeerContext*)client->context;
	WLog_INFO(TAG, "We've got a client %s", client->local ? "(local)" : client->hostname);

	return 0;
}

void rdpsession::setclientPtr(LPVOID arg)
{
    clientPtr = (freerdp_peer*)arg;
}
void rdpsession::settlsCertificate(const std::filesystem::path &newTlsCertificate)
{
    m_tlsCertificate = newTlsCertificate;
}    
void rdpsession::settlsCertificateKey(const std::filesystem::path &newTlsCertificateKey)
{
    m_tlsCertificateKey = newTlsCertificateKey;
}
void rdpsession::start()
{
    clientThread = std::thread(&rdpsession::run, this);
	updateFrameThread= std::thread(&rdpsession::updateFrame, this);
}

void rdpsession::updateFrame()
{
	while(true)
	{
		if(diconnnect)
			break;
		if(ready.load())
		{
			// printf("update frame\n");
			sendFrame();
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(1000) / 25);
	}
}
void rdpsession::sendFrame()
{
	// printf("sendFrame\n");
	int size;
	wStream* s;
	RFX_RECT rect;
	BYTE* rgb_data;
	rdpUpdate* update = clientPtr->update;
	SURFACE_BITS_COMMAND cmd = { 0 };
	testPeerContext* context = (testPeerContext*)clientPtr->context;
	BOOL ret = FALSE;

	if (!clientPtr->settings->RemoteFxCodec && !clientPtr->settings->NSCodec)
		return;

	s = test_peer_stream_init(context);
	rect.x = 0;
	rect.y = 0;
	rect.width = clientPtr->settings->DesktopWidth;
	rect.height = clientPtr->settings->DesktopHeight;
	size = rect.width * rect.height * 3;
	// printf("%d %d\n", rect.width, rect.height);

	if (!(rgb_data = (BYTE*)malloc(size)))
	{
		WLog_ERR(TAG, "Problem allocating memory");
		return;
	}

	memset(rgb_data, rand() %256, size);
	// QImage img("/home/dll/1080.png");
	// if(img.format() != QImage::Format_RGB888)
	// 	img = img.convertToFormat(QImage::Format_RGB888);
	// memcpy(rgb_data, img.bits(), size);

	if (clientPtr->settings->RemoteFxCodec)
	{
		WLog_DBG(TAG, "Using RemoteFX codec");
		if (!rfx_compose_message(context->rfx_context, s, &rect, 1, rgb_data, rect.width,
		                         rect.height, rect.width * 3))
		{
			goto out;
		}

		cmd.bmp.codecID = clientPtr->settings->RemoteFxCodecId;
		cmd.cmdType = CMDTYPE_STREAM_SURFACE_BITS;
	}
	else
	{
		WLog_DBG(TAG, "Using NSCodec");
		nsc_compose_message(context->nsc_context, s, rgb_data, rect.width, rect.height,
		                    rect.width * 3);
		cmd.bmp.codecID = clientPtr->settings->NSCodecId;
		cmd.cmdType = CMDTYPE_SET_SURFACE_BITS;
	}

	cmd.destLeft = 0;
	cmd.destTop = 0;
	cmd.destRight = rect.width;
	cmd.destBottom = rect.height;
	cmd.bmp.bpp = 32;
	cmd.bmp.flags = 0;
	cmd.bmp.width = rect.width;
	cmd.bmp.height = rect.height;
	cmd.bmp.bitmapDataLength = Stream_GetPosition(s);
	cmd.bmp.bitmapData = Stream_Buffer(s);
	test_peer_begin_frame(clientPtr);
	update->SurfaceBits(update->context, &cmd);
	test_peer_end_frame(clientPtr);
	ret = TRUE;
out:
	free(rgb_data);
	return;
}
DWORD WINAPI rdpsession::run()
{
	

	DWORD error = CHANNEL_RC_OK;
	DWORD count;
	HANDLE handles[32];
	DWORD status;

	testPeerContext* context;
	freerdp_peer* client = (freerdp_peer*)clientPtr;
	context = (testPeerContext*)client->context;
	while (error == CHANNEL_RC_OK)
	{
		count = 0;
		{
			DWORD tmp = client->GetEventHandles(client, &handles[count], 32 - count);

			if (tmp == 0)
			{
				WLog_ERR(TAG, "Failed to get FreeRDP transport event handles");
				break;
			}

			count += tmp;
		}
		handles[count++] = WTSVirtualChannelManagerGetEventHandle(context->vcm);
		status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);

		if (status == WAIT_FAILED)
		{
			WLog_ERR(TAG, "WaitForMultipleObjects failed (errno: %d)", errno);
			break;
		}

		if (client->CheckFileDescriptor(client) != TRUE)
			break;

		if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE)
			break;

		/* Handle dynamic virtual channel intializations */
		if (WTSVirtualChannelManagerIsChannelJoined(context->vcm, "drdynvc"))
		{
			switch (WTSVirtualChannelManagerGetDrdynvcState(context->vcm))
			{
				case DRDYNVC_STATE_NONE:
					break;

				case DRDYNVC_STATE_INITIALIZED:
					break;

				case DRDYNVC_STATE_READY:
					ready.store(true);
					// sendFrame();
					break;

				case DRDYNVC_STATE_FAILED:
				default:
					break;
			}
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(1000) / 25);
	}

	WLog_INFO(TAG, "Client %s disconnected.", client->local ? "(local)" : client->hostname);
	diconnnect.store(true);
	updateFrameThread.join();
	client->Disconnect(client);
	freerdp_peer_context_free(client);
	freerdp_peer_free(client);

	return error;
}
